#this file is http://asteriskman.googlecode.com/hg/grooveSharkProxy/grooveSharkSSLProxy.tcl
#this code implements a SSL proxy for listen.grooveshark.com

namespace eval grooveSharkSSLProxy {
  package require dns

  proc binToHex {s} {
    binary scan $s H* hex
    return $hex
  }

  proc nullLog {args} {
  }

  proc openServerListener { portNum {parentLogCmd "nullLog"}} {
    variable listeningSockets
    variable logCmd
    set logCmd $parentLogCmd
    set serverSock [socket -server [namespace code [list openServerInstance $portNum]] $portNum]
    lappend listeningSockets $serverSock
    $logCmd "SSLPROXY: Listening on port $portNum ($serverSock)" debug
  }

  proc closeServerInstances { } {
    variable serverInstances
    set si $serverInstances
    set serverInstances {}
    foreach s $si {
      closeServerInstance $s
    }
  }

  proc closeServerInstance { s } {
    variable socketInfo
    catch {fileevent $s readable ""}
    catch {close $s}
    catch {close $socketInfo($s,fd)}
    array unset socketInfo "$s,*"
  }


  set listeningSockets {}
  proc closeServerListeners { } {
    variable listeningSockets
    set ls $listeningSockets
    set listeningSockets {}
    foreach s $ls {
      close $s
    }
  }

  proc getHostIP {hostname} {
    set token [dns::resolve $hostname]
    dns::wait $token
    set ip [dns::address $token]
    dns::cleanup $token
    return [lindex $ip 0]
  }

  set serverInstances {}
  proc openServerInstance { dstPortNum s srcAddr srcPortNum } {
    variable socketInfo
    variable serverInstances
    variable logCmd
    $logCmd "SSLPROXY: Connection on $s from $srcAddr : $srcPortNum => $dstPortNum" debug
    set socketInfo($s,dstPortNum) $dstPortNum
    set socketInfo($s,srcAddr) $srcAddr
    set socketInfo($s,srcPortNum) $srcPortNum
    set socketInfo($s,type) local
    set socketInfo($s,buffer) {}

    #set dstSocket [socket [getHostIP example.com] $dstPortNum]
    #set dstSocket [socket [getHostIP www.fortify.net] $dstPortNum]
    set dstSocket [socket [getHostIP listen.grooveshark.com] $dstPortNum]
    set socketInfo($dstSocket,dstPortNum) $dstPortNum
    set socketInfo($dstSocket,srcAddr) localhost
    set socketInfo($dstSocket,srcPortNum) [fconfigure $dstSocket -sockname]
    set socketInfo($dstSocket,type) remote
    set socketInfo($dstSocket,buffer) {}

    set socketInfo($s,twin) $dstSocket
    set socketInfo($dstSocket,twin) $s

    lappend serverInstances $s
    lappend serverInstances $dstSocket

    fconfigure $s         -blocking 0 -buffering none -translation binary
    fconfigure $dstSocket -blocking 0 -buffering none -translation binary

    fileevent $s readable [namespace code [list readSocket $s]]
    fileevent $dstSocket readable [namespace code [list readSocket $dstSocket]]

    $logCmd "Proxy Established, waiting for request" debug
  }

  proc readSocket {s} {
    variable socketInfo
    variable logCmd
    fileevent $s readable ""
    if {![eof $s] && [info exists socketInfo($s,type)]} {
      set data [read $s]
      #log "$socketInfo($s,type)([string length $data]): [binToHex $data]\n"
      $logCmd "SSLPROXY: $socketInfo($s,type) ([string length $data])" debug
      if {[catch {puts -nonewline $socketInfo($s,twin) $data} fid]} {
      }
      fileevent $s readable [namespace code [list readSocket $s]]
    } else {
      if {[info exists $socketInfo($s,twin)]} {
        closeServerInstance $socketInfo($s,twin)
      }
      closeServerInstance $s
    }
  }

  #openServerListener 443
}
